using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Reflection;

using GURUCORE.Lib.Core.Security.Cryptography;

namespace GURUCORE.Framework.Core.Factory
{
	public abstract class FactoryBase
	{
		private string m_sDescriptorFile;

		private Hashtable m_htItems;
		private Hashtable m_htFactoryAttributes;
		private Type m_tItemType;
		


        #region CONSTRUCTOR
        
        public FactoryBase(string p_sDescriptorFile, Type p_tItemType)
        {
            m_sDescriptorFile = p_sDescriptorFile;
            m_tItemType = p_tItemType;
            m_htItems = new Hashtable();
            m_htFactoryAttributes = new Hashtable();
            Load();
        }
        #endregion




        private void Load()
        {
            XmlTextReader oReader = null;
            try
            {
                //get factory name, factory item name
                string sFactoryName, sItemName;
                object[] arrAttributes = this.GetType().GetCustomAttributes(typeof(FactoryClassAttribute), false);
                if (arrAttributes.Length > 0)
                {
                    sFactoryName = ((FactoryClassAttribute)arrAttributes[0]).FactoryName;
                    sItemName = ((FactoryClassAttribute)arrAttributes[0]).ItemName;
                }
                else
                {
                    throw new FactoryLoadException("Invalid factory class");
                }

                oReader = new XmlTextReader(m_sDescriptorFile);
                XmlDocument oXmlDoc = new XmlDocument();
                oXmlDoc.Load(oReader);

                //check root element name
                if (oXmlDoc.DocumentElement.Name != sFactoryName)
                {
                    throw new FactoryLoadException(string.Format("oXmlDoc.DocumentElement.Name != sFactoryName,   {0} != {1}", oXmlDoc.DocumentElement.Name, sFactoryName));
                }

                //load all factory attributes
                foreach (XmlAttribute oXmlAttr in oXmlDoc.DocumentElement.Attributes)
                {
                    m_htFactoryAttributes.Add(oXmlAttr.Name, oXmlAttr.Value);
                }

                XmlNodeList arrItemNodes = oXmlDoc.DocumentElement.GetElementsByTagName(sItemName);

                foreach (XmlNode oItemNode in arrItemNodes)
                {
                    FactoryItemBase oItem = (FactoryItemBase)Activator.CreateInstance(m_tItemType);

                    oItem.Name = oItemNode.Attributes["name"].Value.ToString();

                    foreach (XmlNode oPropertyNode in oItemNode.ChildNodes)
                    {
                        if (oPropertyNode.NodeType == XmlNodeType.Element)
                        {
                            string sPropName = oPropertyNode.Name;
                            string sPropValue = oPropertyNode.InnerText.Trim();
                            bool bEncrypted = false;
                            try
                            {
                                bEncrypted = Convert.ToBoolean(oPropertyNode.Attributes["encrypted"].Value);
                            }
                            catch { }

                            if (bEncrypted)
                            {
                                sPropValue = (new CryptoUtil()).Decrypt(sPropValue);
                            }

                            PropertyInfo oProp = m_tItemType.GetProperty(sPropName);

                            if (oProp.PropertyType == typeof(Hashtable))
                            {
                                string sPropKey = oPropertyNode.Attributes["key"].Value.ToString();
                                object objValue = oProp.GetValue(oItem, null);
                                Hashtable htValue;
                                if (objValue != null)
                                {
                                    htValue = (Hashtable)objValue;
                                }
                                else
                                {
                                    htValue = new Hashtable();
                                }
                                htValue.Add(sPropKey, sPropValue);

                                oProp.SetValue(oItem, htValue, null);
                            }
                            else
                            {
                                oProp.SetValue(oItem, sPropValue, null);
                            }
                        }
                    }
                    AddItem(this.m_htFactoryAttributes["id"] + "#" + oItem.Name, oItem);
                }
            }
            catch (Exception ex)
            {
				throw new FactoryLoadException("Factory load error", ex);
            }
            finally
            {
                if (oReader != null)
                {
                    oReader.Close();
                }
            }
        }

        protected void AddItem(string p_sName, FactoryItemBase p_oItem)
        {
            m_htItems[p_sName] = p_oItem;
        }

        protected FactoryItemBase GetItem(string p_sName)
        {
            FactoryItemBase oItem = (FactoryItemBase)m_htItems[p_sName];
            if (null == oItem)
            {
                oItem = (FactoryItemBase)m_htItems[m_htFactoryAttributes["id"] + "#" + p_sName];
            }
            return oItem;
        }

        public ArrayList GetItemNames()
        {
            ArrayList arrItemNames = new ArrayList();
            foreach (DictionaryEntry oEntry in m_htItems)
            {
                arrItemNames.Add(oEntry.Key);
            }
            return arrItemNames;
        }

        /// <summary>
        /// Factory level attributes, declared within Factory-root tag
        /// </summary>
        /// <param name="p_sName"></param>
        /// <returns></returns>
        protected string GetFactoryAttribute(string p_sName)
        {
            return m_htFactoryAttributes[p_sName].ToString();
        }

        /// <summary>
        /// Merge with another factory, must be the same type
        /// </summary>
        /// <param name="p_oOther"></param>
        public void Merge(FactoryBase p_oOther)
        {
            Type tThis = this.GetType();
            Type tOther = p_oOther.GetType();
            if (tThis != tOther)
            {
                throw new Exception("Factory type mismatch");
            }

            foreach (DictionaryEntry oEntry in p_oOther.m_htFactoryAttributes)
            {
                if ((!this.m_htFactoryAttributes.ContainsKey(oEntry.Key)) && (oEntry.Key.ToString() != "id"))
                {
                    this.m_htFactoryAttributes.Add(oEntry.Key, oEntry.Value);
                }
            }

            //string sFactoryID = p_oOther.GetFactoryAttribute("id");
            foreach (DictionaryEntry oEntry in p_oOther.m_htItems)
            {
                //this.m_htItems.Add(sFactoryID + "#" + oEntry.Key, oEntry.Value);
                this.m_htItems.Add(oEntry.Key, oEntry.Value);
            }
        }

	}
}
